#pragma once
#include <Rz/IObject.h>
#include <Rz/Graphics/Render/IRenderDevice.h>
#include <Rz/Graphics/Render/IDeviceResource.h>
#include <Rz/Graphics/Render/IBindableResource.h>

namespace Rz { namespace Graphics { namespace Render {

enum class StencilOperation
{
	Keep,
	Zero,
	Replace,
	Increment,
	Decrement,
	IncrementSaturation,
	DecrementSaturation,
	Invert
};

enum class CompareFunction
{
	Always,
	Never,
	Less,
	LessEqual,
	Equal,
	GreaterEqual,
	Greater,
	NotEqual
};

class IDepthStencilState :
	virtual public IObject,
	virtual public IDeviceResource,
	virtual public IBindableResource
{
	public:
		RZ_RTTI_INTERFACE(Rz::Graphics::Render, IDepthStencilState);
		RZ_RTTI_IMPLEMENTS(IObject, IDeviceResource, IBindableResource);
		RZ_RTTI_END;
		
		virtual void Bind() override { _isBound = true; }
		virtual void Unbind() override { _isBound = false; }
		virtual bool IsBound() const { return _isBound; }

		virtual StencilOperation GetCounterClockwiseStencilDepthBufferFail() const { return _counterClockwiseStencilDepthBufferFail; }
		virtual void SetCounterClockwiseStencilDepthBufferFail(StencilOperation operation) { _counterClockwiseStencilDepthBufferFail = operation; }

		virtual StencilOperation GetCounterClockwiseStencilFail() const { return _counterClockwiseStencilFail; }
		virtual void SetCounterClockwiseStencilFail(StencilOperation operation) { _counterClockwiseStencilFail = operation; }

		virtual CompareFunction GetCounterClockwiseStencilFunction() const { return _counterClockwiseStencilFunction; }
		virtual void SetCounterClockwiseStencilFunction(CompareFunction function) { _counterClockwiseStencilFunction = function; }

		virtual StencilOperation GetCounterClockwiseStencilPass() const { return _counterClockwiseStencilPass; }
		virtual void SetCounterClockwiseStencilPass(StencilOperation operation) { _counterClockwiseStencilPass = operation; }

		virtual bool GetDepthBufferEnable() const { return _depthBufferEnable; }
		virtual void SetDepthBufferEnable(bool state) { _depthBufferEnable = state; }

		virtual CompareFunction GetDepthBufferFunction() const { return _depthBufferFunction; }
		virtual void SetDepthBufferFunction(CompareFunction function) { _depthBufferFunction = function; }

		virtual bool GetDepthBufferWriteEnable() const { return _depthBufferWriteEnable; }
		virtual void SetDepthBufferWriteEnable(bool state) { _depthBufferWriteEnable = state; }

		virtual s32 GetReferenceStencil() const { return _referenceStencil; }
		virtual void SetReferenceStencil(s32 state) { _referenceStencil = state; }

		virtual StencilOperation GetStencilDepthBufferFail() const { return _stencilDepthBufferFail; }
		virtual void SetStencilDepthBufferFail(StencilOperation operation) { _stencilDepthBufferFail = operation; }

		virtual bool GetStencilEnable() const { return _stencilEnable; }
		virtual void SetStencilEnable(bool state) { _stencilEnable = state; }

		virtual StencilOperation GetStencilFail() const { return _stencilFail; }
		virtual void SetStencilFail(StencilOperation operation) { _stencilFail = operation; }

		virtual CompareFunction GetStencilFunction() const { return _stencilFunction; }
		virtual void SetStencilFunction(CompareFunction function) { _stencilFunction = function; }

		virtual s32 GetStencilMask() const { return _stencilMask; }
		virtual void SetStencilMask(s32 mask) { _stencilMask = mask; }

		virtual StencilOperation GetStencilPass() const { return _stencilPass; }
		virtual void SetStencilPass(StencilOperation operation) { _stencilPass = operation; }

		virtual s32 GetStencilWriteMask() const { return _stencilWriteMask; }
		virtual void SetStencilWriteMask(s32 mask) { _stencilWriteMask = mask; }

		virtual bool GetTwoSidedStencilMode() const { return _twoSidedStencilMode; }
		virtual void SetTwoSidedStencilMode(bool state) { _twoSidedStencilMode = state; }

	protected:
		IDepthStencilState()
			: _isBound(false)

			// BackFace
			, _counterClockwiseStencilDepthBufferFail(StencilOperation::Keep)
			, _counterClockwiseStencilFail(StencilOperation::Keep)
			, _counterClockwiseStencilFunction(CompareFunction::Always)
			, _counterClockwiseStencilPass(StencilOperation::Keep)

			// FrontFace
			, _stencilDepthBufferFail(StencilOperation::Keep)
			, _stencilFail(StencilOperation::Keep)
			, _stencilFunction(CompareFunction::Always)
			, _stencilPass(StencilOperation::Keep)

			, _depthBufferEnable(true)
			, _depthBufferFunction(CompareFunction::LessEqual)
			, _depthBufferWriteEnable(true)
			, _referenceStencil(0)
			, _stencilEnable(false)
			, _stencilMask(0x7FFFFFFF)		// INT_MAX
			, _stencilWriteMask(0x7FFFFFFF)	// INT_MAX
			, _twoSidedStencilMode(false)
		{

		}

	private:
		bool _isBound;
		StencilOperation _counterClockwiseStencilDepthBufferFail;
		StencilOperation _counterClockwiseStencilFail;
		CompareFunction _counterClockwiseStencilFunction;
		StencilOperation _counterClockwiseStencilPass;
		bool _depthBufferEnable;
		CompareFunction _depthBufferFunction;
		bool _depthBufferWriteEnable;
		s32 _referenceStencil;
		StencilOperation _stencilDepthBufferFail;
		bool _stencilEnable;
		StencilOperation _stencilFail;
		CompareFunction _stencilFunction;
		u32 _stencilMask;
		StencilOperation _stencilPass;
		u32 _stencilWriteMask;
		bool _twoSidedStencilMode;
};

} } }
